注意:所有文章除特别说明外,转载请注明出处.
第20章 FlashMapManager
FlashMapManager用来管理FlashMap,FlashMap用于在redirect时传递参数。FlashMapManager实现结构简单,只有一个抽象类和一个实现类。抽象类采用模板模式定义整体流程。具体实现类SessionFlashManager通过模板方法提供具体操作FlashMap的功能。
说明:1.实际的Session中保存的FlashMap类型是List
类型,也就是说一个Session可以保存多个FlashMap,一个FlashMap保存着一套Redirect转发所传递的参数。2. FlashMap继承自HashMap,除了用于HashMap的功能和设置有效期,还可以保存Redirect后的目标路径和通过url传递的参数,这两项内容主要用来从Session保存的多个FlashMap中查找当前的FalshMap。
1.AbstractFlashMapManager
1.1 saveOutputFlashMap
public final void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {
if (!CollectionUtils.isEmpty(flashMap)) {
//首先对flashMap中转发的地址和参数进行编码,这里的request主要是用来获取当前的编码
String path = this.decodeAndNormalizePath(flashMap.getTargetRequestPath(), request);
flashMap.setTargetRequestPath(path);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Saving FlashMap=" + flashMap);
}
//设置过期时间,默认值是180秒
flashMap.startExpirationPeriod(this.getFlashMapTimeout());
用于获取互斥变量,是模板方法,如果子类返回不为null则同步执行,否则不需要同步
Object mutex = this.getFlashMapsMutex(request);
if (mutex != null) {
synchronized(mutex) {
//取回保存的flashMap,如果没有则新建一个
List<FlashMap> allFlashMaps = this.retrieveFlashMaps(request);
List<FlashMap> allFlashMaps = allFlashMaps != null ? allFlashMaps : new CopyOnWriteArrayList();
((List)allFlashMaps).add(flashMap);
//将添加完的list<FalshMap>更新到存储介质中去,是模板方法,由子类实现
this.updateFlashMaps((List)allFlashMaps, request, response);
}
} else {
List<FlashMap> allFlashMaps = this.retrieveFlashMaps(request);
List<FlashMap> allFlashMaps = allFlashMaps != null ? allFlashMaps : new LinkedList();
((List)allFlashMaps).add(flashMap);
this.updateFlashMaps((List)allFlashMaps, request, response);
}
}
}
总结:1.首先对flashMap中的目标地址和url参数进行编码,编码格式使用当前request获取。2.然后设置有效期,通过flashMapTimeout参数配置。3.最后将flashMap添加到整体的List
中并更新。
1.2 SessionFlashMapManager
public class SessionFlashMapManager extends AbstractFlashMapManager {
private static final String FLASH_MAPS_SESSION_ATTRIBUTE = SessionFlashMapManager.class.getName() + ".FLASH_MAPS";
public SessionFlashMapManager() {
}
@Nullable
protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {
HttpSession session = request.getSession(false);
return session != null ? (List)session.getAttribute(FLASH_MAPS_SESSION_ATTRIBUTE) : null;
}
protected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) {
WebUtils.setSessionAttribute(request, FLASH_MAPS_SESSION_ATTRIBUTE, !flashMaps.isEmpty() ? flashMaps : null);
}
protected Object getFlashMapsMutex(HttpServletRequest request) {
return WebUtils.getSessionMutex(request.getSession());
}
}
1.3 retrieveAndUpdate
public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {
//从存储介质session中获取list<FlashMap>,模板方法,子类实现
List<FlashMap> allFlashMaps = this.retrieveFlashMaps(request);
if (CollectionUtils.isEmpty(allFlashMaps)) {
return null;
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Retrieved FlashMap(s): " + allFlashMaps);
}
//检查过期的flashMap,并将那他们设置到mapsToRemove
List<FlashMap> mapsToRemove = this.getExpiredFlashMaps(allFlashMaps);
//获取与当前request匹配的FlashMap,并设置到match中
FlashMap match = this.getMatchingFlashMap(allFlashMaps, request);
//如果有匹配的则将其添加到mapsToRemove,待下面删除
if (match != null) {
mapsToRemove.add(match);
}
//删除mapsToRemove中保存的变量
if (!mapsToRemove.isEmpty()) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Removing FlashMap(s): " + mapsToRemove);
}
Object mutex = this.getFlashMapsMutex(request);
if (mutex != null) {
synchronized(mutex) {
allFlashMaps = this.retrieveFlashMaps(request);
if (allFlashMaps != null) {
allFlashMaps.removeAll(mapsToRemove);
this.updateFlashMaps(allFlashMaps, request, response);
}
}
} else {
allFlashMaps.removeAll(mapsToRemove);
this.updateFlashMaps(allFlashMaps, request, response);
}
}
return match;
}
}
总结:上面的过程是:1.首先使用retrieveFlashMaps模板方法获取List
。2.然后检查其中已经过期的FlashMap并保存,检查方法通过保存时设置的过期时间进行判断。3.然后调用getMatchingFlashMap()方法从获取的List 中找出和当前request相匹配的FlashMap。4.最后将过期的和当前请求匹配的FlashMap从List 中删除并更新到Session中,将与当前request匹配的返回。
1.4 getMatchingFlashMap()
该方法是查找与当前request匹配的FlashMap。
private FlashMap getMatchingFlashMap (List<FlashMap> allMaps, HttpServletRequest request) {
List<FlashMap> result = new LinkedList<FlashMap>();
for(FlashMap flashMap : allMaps){
//1.调用isFlashMapForRequest()方法实际检查是否匹配,如果匹配则保存到临时变量result
if(isFlashMapForRequest(flashMap, request)){
result.add(flashMap);
}
}
if(!result.isEmpty()){
Collections.sort(result);
if(logger.isDebugEnable){
logger.debug("");
}
return result.get(0);
}
return null;
}
1.5 isFlashMapForRequest()
protected boolean isFlashMapForRequest(FlashMap flashMap, HttpServletRequest request) {
//检查目标路径,如果flashMap中保存的和request的不匹配,返回false
String expectedPath = flash.getTargetRequestpath();
if(expectedPath != null){
String requestUri = getUrlPathHelper().getOriginatingRequestUri(request);
if(!requestUri.equals(expectedPath) && !requestUri.equals(expectedPath + "/")){
return false;
}
}
//检查参数,如果在FlashMap中保存的参数在request中没有则返回false
MultiValueMap<String, String> targetParams = flashMap.getTargetRequestParams();
for(String expectedName : targetParams.keySet()){
if(!ObjectUtils.containsElement(request.getParameterValues(expectedName), expectedValue)){
return false;
}
}
return true;
}
总结:这里的检查方法是通过FlashMap中保存的目标地址和url参数与request进行比较的。